﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.Mirror.CliMetadata;

namespace Kokomo.Mirror.CliMetadata
{
	public struct ModuleMemberReference : IEquatable<ModuleMemberReference>
	{
		public static readonly ModuleMemberReference Null = new ModuleMemberReference();

		public ModuleMemberReference(
			MemberInfo member
			)
			: this(member.Module, member.MetadataToken)
		{
			//this._member = new WeakReference(member);
		}
		private ModuleMemberReference(
			Module module,
			int token
			)
		{
			//if (module == null) throw new ArgumentNullException("module");

			this.Module = module;
			this.MetadataToken = token;
			//#if DEBUG
			//            if (CodedIndex.IsReferenceToken(token)
			//                && (CodedIndex.TableFromToken(token) != MetadataTableNumber.TypeSpec)
			//                )
			//            {
			//                MemberInfo member = this.LoadMember();
			//                Type type = member as Type;
			//                MethodInfo method = member as MethodInfo;
			//                if (
			//                    ((type != null) && (!type.IsArray) && (!type.HasElementType))
			//                    || ((method != null) && (!method.DeclaringType.IsArray))
			//                    )
			//                {
			//                }
			//            }
			//#endif
		}

		public static ModuleMemberReference FromModuleToken(
			Module module,
			int token
			)
		{
			if ((object)module == null) throw new ArgumentNullException("module");

			MetadataTableNumber tableNumber = CodedIndex.FromToken(token).TableNumber;
			ModuleMemberReference reference = new ModuleMemberReference(module, token);

			return reference;
		}

		//public Module Module { get; private set; }
		//public int MetadataToken { get; private set; }
		public readonly Module Module;
		public readonly int MetadataToken;

		public bool IsCanonical
		{
			get { return CodedIndex.IsCanonicalToken(this.MetadataToken); }
		}
		public bool IsReference
		{
			get { return CodedIndex.IsReferenceToken(this.MetadataToken); }
		}

		public override bool Equals(object obj)
		{
			if (!(obj is ModuleMemberReference)) return false;
			ModuleMemberReference other = (ModuleMemberReference)obj;

			//bool equals =
			//    ((object)this.Module != null)
			//    && ((object)other.Module != null)
			//    && (this.Module.ScopeName == other.Module.ScopeName)
			//    && (this.MetadataToken == other.MetadataToken)
			//    ;
			//return equals;
			return this.Equals(other);
		}

		public override int GetHashCode()
		{
			int hash = this.MetadataToken;
			//(this.Module != null) ? this.Module.ScopeName.GetHashCode() : 0
			//^ this.Token
			//;
			return hash;
		}

		public MemberInfo LoadMember()
		{
			//MemberInfo member = (MemberInfo)this._member.Target;
			//if (member == null)
			//{
			//    member = this.Module.ResolveMember(this.MetadataToken);
			//    this._member = new WeakReference(member);
			//}

			//return member;
			if (IsEmpty(this))
				throw new InvalidOperationException("Reference is invalid");

			return this.Module.ResolveMember(this.MetadataToken);
		}

		public static bool IsEmpty(ModuleMemberReference reference)
		{
			return ((object)reference.Module == null);
		}

		public bool Matches(MemberInfo member)
		{
			bool equals =
				((object)member != null)
				&& ((object)member.Module != null)
				&& ((object)this.Module != null)
				&& (this.MetadataToken == member.MetadataToken)
				&& (this.Module.ScopeName == member.Module.ScopeName)
				;
			return equals;
		}

		public bool Equals(ModuleMemberReference other)
		{
			bool equal =
				(other.Module == this.Module)
				&& (other.MetadataToken == this.MetadataToken);
			return equal;
		}

		public ModuleMemberReference MakeCanonical()
		{
			if (this.IsCanonical)
				return this;

			MemberInfo member = this.Module.ResolveMember(this.MetadataToken);
			ModuleMemberReference canonical;
			// For special members, refer to the MemberRef,
			// since there is no definition
			if ((member.MetadataToken & 0x00FFFFFF) != 0)
				canonical = new ModuleMemberReference(member);
			else
				canonical = ModuleMemberReference.FromModuleToken(this.Module, this.MetadataToken);

			return canonical;
		}
	}
}
